package util

import (
	"errors"
	"strconv"
	"strings"
)

type NamingStrategy interface {
	ColumnToField(s string) string
	FieldToColumn(s string) string
}

const (
	NamingStrategySnake = iota + 1
)

func GetNamingStrategy(strategy int) NamingStrategy {
	switch strategy {
	case NamingStrategySnake:
		return &snakeNamingStrategy{}
	default:
		panic(errors.New("valid argument '" + strconv.Itoa(strategy) + "'"))
	}
}

type snakeNamingStrategy struct {
}

func (ns *snakeNamingStrategy) ColumnToField(src string) string {
	r := []rune(src)
	data := make([]rune, 0, len(r))
	j := false
	k := false
	num := len(r) - 1
	for i := 0; i <= num; i++ {
		d := r[i]
		if k == false && d >= 'A' && d <= 'Z' {
			k = true
		}
		if d >= 'a' && d <= 'z' && (j || k == false) {
			d = d - 32
			j = false
			k = true
		}
		if k && d == '_' && num > i && r[i+1] >= 'a' && r[i+1] <= 'z' {
			j = true
			continue
		}
		data = append(data, d)
	}
	return string(data[:])
}

func (ns *snakeNamingStrategy) FieldToColumn(src string) string {
	r := []rune(src)
	data := make([]rune, 0, len(r)*2)
	j := false
	num := len(r)
	for i := 0; i < num; i++ {
		d := r[i]
		if i > 0 && d >= 'A' && d <= 'Z' && j {
			data = append(data, '_')
		}
		if d != '_' {
			j = true
		}
		data = append(data, d)
	}
	return strings.ToLower(string(data[:]))
}
